#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "err.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"

namespace Alpaca
{
    static const uint16_t defaultport = 8080;
    static const int backlog = 32;
    using func_t = std::function<std::string(const std::string &)>;

    class TcpServer
    {
    public:
        class ThreadData
        {
        public:
            ThreadData(int fd, const std::string &ip, const uint16_t port, TcpServer *ts)
                : sock(fd), clientip(ip), clientport(port), current(ts)
            {
            }
            int sock;
            std::string clientip;
            uint16_t clientport;
            TcpServer *current;
        };

        TcpServer(func_t func, uint16_t port = defaultport) : _func(func), _port(port), _quit(true)
        {
        }

        ~TcpServer()
        {
        }

        void InitServer()
        {
            // 创建套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                std::cerr << "create sock error" << std::endl;
                logmessage(FATAL, "create sock error %d:%s", errno, strerror(errno));
                exit(SOCKET_ERR);
            }
            logmessage(INFO, "create sock success");
            // 绑定端口
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (sockaddr *)&local, sizeof(local)) < 0)
            {
                std::cerr << "bind error" << std::endl;
                logmessage(FATAL, "bind error %d:%s", errno, strerror(errno));
                exit(BIND_ERR);
            }
            logmessage(INFO, "bind success");

            // 监听
            if (listen(_listensock, backlog) < 0)
            {
                std::cerr << "listen error" << std::endl;
                logmessage(WARNING, "listen error %d:%s", errno, strerror(errno));
                exit(LISTEN_ERR);
            }
            logmessage(INFO, "listen success");
        }

        void start()
        {
            signal(SIGCHLD, SIG_IGN);
            _quit = false;
            while (!_quit)
            {
                // 获取连接
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int sock = accept(_listensock, (sockaddr *)&client, &len);
                if (sock < 0)
                {
                    std::cout << "accept error" << std::endl;
                    continue;
                }
                std::string clientip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);

                // 获取新连接成功
                std::cout << "获取新连接成功 " << sock << " form " << _listensock << ", "
                          << clientip << "-" << clientport << std::endl;
                logmessage(INFO, "获取新连接成功:%d from %d,who:%s - %d",
                           sock, _listensock, clientip.c_str(), clientport);
                // service(sock, clientip, clientport); 单进程

                // // 多进程
                // pid_t id = fork();
                // if (id < 0)
                // {
                //     close(sock);
                //     continue;
                // }
                // else if (id == 0)
                // {
                //     close(_listensock);
                //     // if(fork()>0) exit(0);
                //     service(sock, clientip, clientport);
                //     exit(0);
                // }
                // // 父进程,不关闭则会造成fd泄漏
                // close(sock);

                // // 多线程
                // pthread_t tid;
                // ThreadData *td = new ThreadData(sock, clientip, clientport, this);
                // pthread_create(&tid, nullptr, threadRoutine, td);

                // 线程池
                // 线程池中线程的个数的是有限的，一定是处理短任务
                // 因此，可以将线程池置于多线程之中，线程回调的时候再使用
                Task t(sock, clientip, clientport, std::bind(&TcpServer::service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                ThreadPool<Task>::getinstance()->pushtask(t);
            }
        }

        static void *threadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);

            td->current->service(td->sock, td->clientip, td->clientport);
            delete td;
        }

        void service(int sock, const std::string &clientip, const uint16_t &clientport)
        {
            std::string who = clientip + "-" + std::to_string(clientport);
            char buffer[1024];
            while (true)
            {
                ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                if (s > 0)
                {
                    buffer[s] = '\0';
                    std::string res = _func(buffer);
                    std::cout << who << " >>> " << res << std::endl;
                    logmessage(DEBUG, "%s# %s", who.c_str(), res.c_str());
                    write(sock, res.c_str(), res.size());
                }
                else if (s == 0) // 对方把连接关闭
                {
                    close(sock);
                    std::cout << who << "quit, me too" << std::endl;
                    logmessage(INFO, "%s quit, me too", who.c_str());
                    break;
                }
                else // 读失败
                {
                    close(sock);
                    std::cout << "read error" << strerror(errno) << std::endl;
                    logmessage(ERROR, "read error %d:%s", errno, strerror(errno));
                    break;
                }
            }
        }

    private:
        int _listensock;
        uint16_t _port;
        bool _quit;
        func_t _func;
    };
}